package net.cyndeline.stateEvaluation.defaultImpl

import net.cyndeline.stateEvaluation.PropertyCondition
import net.cyndeline.stateEvaluation.Evaluator
import net.cyndeline.stateEvaluation.Junction
import net.cyndeline.itemEventFactory.ActionInput
import net.cyndeline.entitySystem.EntityRef
import net.cyndeline.stateEvaluation.OR
import net.cyndeline.stateEvaluation.AND
import net.cyndeline.stateEvaluation.AND_OR

class DefaultEvaluator extends Evaluator {
  
  /* Every property must hold. */
  private var and = Set[PropertyCondition]()
  
  /* One property from every set must hold. */
  private var or = List[Set[PropertyCondition]]()
  
  /* Every property from one set must hold. */
  private var andOr = List[Set[PropertyCondition]]()
  
  override def addConditions(conditions: Set[PropertyCondition], junction: Junction) = junction match {
    case AND => and = and ++ conditions
    case OR => or = or :+ conditions
    case AND_OR => andOr = andOr :+ conditions
  }
  
  override def evaluate(data: ActionInput): Set[EntityRef] = {
    
    /* Begin with a full set, then subtract any entities that doesn't fulfill
     * the evaluation.
     */
    var result = evaluateEntities(data.allInput.toList, data)    
    result
  }
  
  private def evaluateEntities(entities: List[EntityRef], data: ActionInput): Set[EntityRef] = entities match {
      case entity::rest => {
        
        /* Remove the entity if it doesn't pass all AND tests. */
        if (!allConditionsValidates(and, entity, data)) {
          return evaluateEntities(rest, data)
          
        } else {
          
          /* If it does, remove it if it doesn't pass at least one test per OR
           * set.
           */
          for (propertySet <- or) {
            if (!oneConditionValidates(propertySet, entity, data)) {
              return evaluateEntities(rest, data)
            }
          }
          
          /* If it does, remove it if it doesn't pass every test from at least
           * one andOr set.
           */
          for (propertySet <- andOr) {
            if (allConditionsValidates(propertySet, entity, data)) {
              
              /* If this check passes one set, the entity validates.
               * Add it to the result and continue with the next.
               */
              return Set(entity) | evaluateEntities(rest, data)
            }
          }
          
          /* Catches any entity that passes due to no andOr sets. */
          return Set(entity) | evaluateEntities(rest, data)
        }
      }
      
      case Nil => Set()
  }
  
  
  private def oneConditionValidates(conditions: Set[PropertyCondition], entity: EntityRef, data: ActionInput): Boolean = {
    for (c <- conditions) {
      if (c.fulfillsCondition(entity, data)) {
        return true
      }
    }
    
    false
  }
  
  private def allConditionsValidates(conditions: Set[PropertyCondition], entity: EntityRef, data: ActionInput): Boolean = {
    for (c <- conditions) {
      if (!c.fulfillsCondition(entity, data)) {
        return false
      }
    }
    
    true
  }

}